﻿using System;
using System.Collections.Generic;

using System.Text;
using System.Diagnostics;
using System.Collections;
using System.IO;
using System.Data;
using System.Data.Common;

namespace DistributedCacheDataProvider
{
    public class DistributedCacheDataReader : DbDataReader, IDataReader, IDataRecord, IEnumerable, IDisposable
    {
        internal DistributedCacheDataReader()
		{
			Debug.WriteLine("DistributedCacheDataReader.constructor", "DistributedCacheDataReader");
		}

		internal DistributedCacheDataReader(String command)
		{
			Debug.WriteLine("DistributedCacheDataReader.constructor", "DistributedCacheDataReader(cmd)");
		}

		internal DistributedCacheDataReader(String command, IDbConnection conn)
		{
			Debug.WriteLine("DistributedCacheDataReader.constructor", "DistributedCacheDataReader(cmd, conn)");
			_Connection = conn;
		}

		void NotSupported()
		{
			throw new System.NotSupportedException();
		}

		// IDataReader methods

		public override DataTable GetSchemaTable() 
		{ 
			Debug.WriteLine("DistributedCacheDataReader.GetSchemaTable", "DistributedCacheDataReader");

			DataTable tab = new DataTable();

			// all of common, non "base-table" fields implemented
			tab.Columns.Add("ColumnName", typeof(System.String));
			tab.Columns.Add("ColumnSize", typeof(Int32));
			tab.Columns.Add("ColumnOrdinal", typeof(Int32));
			tab.Columns.Add("NumericPrecision", typeof(Int16));
			tab.Columns.Add("NumericScale", typeof(Int16));
			tab.Columns.Add("DataType", typeof(System.Type));
			tab.Columns.Add("AllowDBNull", typeof(bool));
			tab.Columns.Add("IsReadOnly", typeof(bool));
			tab.Columns.Add("IsUnique", typeof(bool));
			tab.Columns.Add("IsRowVersion", typeof(bool));
			tab.Columns.Add("IsKey", typeof(bool));
			tab.Columns.Add("IsAutoIncrement", typeof(bool));
			tab.Columns.Add("IsLong", typeof(bool));

			for (int i=0;i < _fieldCount;i++)
			{
				DataRow r = tab.NewRow();
				r["ColumnName"] = _names[i];
				r["ColumnSize"] = _sizes[i];
				r["ColumnOrdinal"] = i;			
				r["NumericPrecision"] = 0;
				r["NumericScale"] = 0;
				r["DataType"] = _types[i];
				r["AllowDBNull"] = false;
				r["IsReadOnly"] = true;
				r["IsUnique"] = false;
				r["IsRowVersion"] = false;
				r["IsKey"] = false;
				r["IsAutoIncrement"] = false;
				r["IsLong"] = false;

				// specializations, filename is unique
				if (_names[i] == "Name")
					r["IsUnique"] = true;

				tab.Rows.Add(r);
			}
			return tab;
		}

		public override void Close() 
		{
			Debug.WriteLine("DistributedCacheDataReader.Close", "DistributedCacheDataReader");
			_isClosed = true;		
	
			// close the connection if it was opened with the
			// CommandBehavior.CommandClose 
			if (_Connection != null)
				_Connection.Close();
		}

		public override bool NextResult()
		{
			Debug.WriteLine("DistributedCacheDataReader.NextResult", "DistributedCacheDataReader");
			return false;	
		}

		public override bool Read() 
		{
			Debug.WriteLine("DistributedCacheDataReader.Read", "DistributedCacheDataReader");
			if (_ie != null)
			{
				bool notEOF = _ie.MoveNext();
				if (notEOF == true)
				{
					_CurrentRow++;
					if (_fsi[_CurrentRow] is FileInfo)
					{
						FileInfo f = (FileInfo)_fsi[_CurrentRow];
						_cols[0] = f.Name;
						_cols[1] = f.Length; // changed from string
						_cols[2] = "File";
                        _cols[3] = f.CreationTime; // changed from string
					}
					else
					{
						DirectoryInfo d = (DirectoryInfo)_fsi[_CurrentRow];
						_cols[0] = d.Name;
                        _cols[1] = 0; // changed from string
						_cols[2] = "Directory";
                        _cols[3] = d.CreationTime; // changed from string
					}
				}
				return notEOF;
			}

			return false;
		}

		// IDataReader properties

		int _depth = 0;
		public override int Depth 
		{
			get 
			{
				Debug.WriteLine("DistributedCacheDataReader.get_Depth", "DistributedCacheDataReader");
				return _depth;
			}
		}

		bool _isClosed = false;
		public override bool IsClosed
		{
			get 
			{
				Debug.WriteLine("DistributedCacheDataReader.get_IsClosed", "DistributedCacheDataReader");
				return _isClosed;
			}
		}

		int _RecordsAffected;
		public override int RecordsAffected
		{
			get 
			{
				Debug.WriteLine("DistributedCacheDataReader.get_RecordsAffected", "DistributedCacheDataReader"); 
				return _RecordsAffected;
			}
		}

		// IDataRecord

		public override bool GetBoolean(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetBoolean", "DistributedCacheDataReader");
			return (bool)_cols[i];
		}

		public override byte GetByte(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetByte", "DistributedCacheDataReader");
			return (byte)_cols[i];
		}

		public override long GetBytes(int i, long fieldoffset, byte[] buffer, int length, int bufferoffset)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetBytes", "DistributedCacheDataReader");
			NotSupported();
			return 0;	
		}

		public override char GetChar(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetChar", "DistributedCacheDataReader");
			return (char)_cols[i];
		}

		public override long GetChars(int i, long fieldoffset, char[] buffer, int length, int bufferoffset)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetChars", "DistributedCacheDataReader");
			NotSupported();
			return 0;	
		}

		/*
		public override IDataReader GetData(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetData", "DistributedCacheDataReader");
			// why does this return not supported rather than invalid cast?
			// in SDK
			NotSupported();
			return (IDataReader)_cols[i];

		}
		*/

		public override string GetDataTypeName(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetDataTypeName", "DistributedCacheDataReader");
			return _types[i].ToString();
		}

		public override DateTime GetDateTime(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetDateTime", "DistributedCacheDataReader");
			return (DateTime)_cols[i];
		}

		public override decimal GetDecimal(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetDecimal", "DistributedCacheDataReader");
			return (decimal)_cols[i];;
		}

		public override double GetDouble(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetDouble", "DistributedCacheDataReader");
			return (double)_cols[i];;
		}

		public override Type GetFieldType(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetFieldType", "DistributedCacheDataReader");
			return _types[i];
		}

		public override float GetFloat(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetFloat", "DistributedCacheDataReader");
			return (float)_cols[i];
		}

		public override Guid GetGuid(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetGuid", "DistributedCacheDataReader");
			return (Guid)_cols[i];
		}

		public override short GetInt16(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetInt16", "DistributedCacheDataReader");
			return (short)_cols[i];
		}

		public override int GetInt32(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetInt32", "DistributedCacheDataReader");
			return (int)_cols[i];
		}

		public override long GetInt64(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetInt64", "DistributedCacheDataReader");
			return (long)_cols[i];
		}

		public override string GetName(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetName", "DistributedCacheDataReader");
			return _names[i];
		}

		public override int GetOrdinal(string name)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetOrdinal", "DistributedCacheDataReader");
			return Array.IndexOf(_names, name);
		}

		public override string GetString(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetString", "DistributedCacheDataReader");
			return (String)_cols[i];
		}

		public override object GetValue(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetValue", "DistributedCacheDataReader");	
			return _cols[i];
		}

		public override int GetValues(object[] values)
		{
			Debug.WriteLine("DistributedCacheDataReader.GetValues", "DistributedCacheDataReader");
			for (int i=0;i<_fieldCount;i++)
				values[i] = _cols[i];
			return _fieldCount;
		}

		public override bool IsDBNull(int i)
		{
			Debug.WriteLine("DistributedCacheDataReader.IsDBNull", "DistributedCacheDataReader");
			return false;	
		}

		public override bool HasRows
		{
			get
			{
				Debug.WriteLine("DistributedCacheDataReader.get_HasRows", "DistributedCacheDataReader");
				return (_RecordsAffected > 0);
			}
		}

		// IDataRecord properties

		int _fieldCount = 4;
		public override int FieldCount
		{
			get 
			{
				Debug.WriteLine("DistributedCacheDataReader.get_FieldCount", "DistributedCacheDataReader");
				return _fieldCount;
			}
		}

		// This is property Item(String)
		public override object this[string name]
		{
			get 
			{
				Debug.WriteLine("DistributedCacheDataReader.get_this[string]", "DistributedCacheDataReader");
				return this[Array.IndexOf(_names, name)];
			}
		}

		// This is property Item(Int32)
		public override object this[int i]
		{
			get 
			{
				Debug.WriteLine("DistributedCacheDataReader.get_this[int]", "DistributedCacheDataReader");
				return _cols[i];	
			}
		}

		public override IEnumerator GetEnumerator()
		{
			return ((IEnumerator) new DbEnumerator(this));
		}

		// IEnumerable method

		IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return ((IEnumerator) new DbEnumerator(this));
		}

		// Implementation

		/* 
		 * We keep track of the connection to implement the
		 * CommandBehavior.CloseConnection flag. null means
		 * normal behavior (don't automatically close)
		 */
		private   IDbConnection _Connection = null;

		internal  DirectoryInfo _dir; 
		internal  FileSystemInfo[] _fsi; 
		internal  int _CurrentRow;
		internal  IEnumerator _ie;

		internal  String[] _names = {"Name", "Size", "Type", "CreationDate" };
		internal  Type[] _types = {typeof(String), typeof(long), typeof(String), typeof(DateTime)};			
		internal  object[] _cols = new object[4];

		// maximum size in bytes
		internal  Int32[] _sizes = { 1024, 8, 9, 8 };


		internal void GetDirectory(String command)
		{
			_dir = new DirectoryInfo(command);
			_fsi = _dir.GetFileSystemInfos();

			_RecordsAffected = _fsi.Length;
			
			_CurrentRow = -1;
			_ie = _fsi.GetEnumerator();
			_isClosed = false;
		}
    }
    
}
